using System;

namespace IntelHexConverter
{
	internal class IntelHexDataSet
	{
		byte m_type = 0;
		byte m_checksum = 0;
		byte m_data_length = 0;
		int m_address = 0;
		byte[] m_data = new byte[0];
			
		string m_raw_data = "";
		
		ConversionResult m_last_conversion_result = ConversionResult.Unknown;
		
		public IntelHexDataSet(string line)
		{
			if(CheckLine(line) != ConversionResult.OK)
				throw new Exception();
			
			m_raw_data = line;
		}
		
		#region public
		public ConversionResult ParseLine()
		{
			byte[] full_set;
			
			if(CheckLine(m_raw_data) != ConversionResult.OK)
				throw new Exception();
			
			full_set = StringToByte(m_raw_data.Substring(1));
			
			m_data_length = StringToByte(m_raw_data.Substring(1,2))[0];
			m_address = BitConverter.ToInt16(StringToByte(m_raw_data.Substring(3,4)),0);
			m_type = StringToByte(m_raw_data.Substring(7,2))[0];
			
			if(m_raw_data.Length < (9+2*m_data_length+2))
			{
				m_last_conversion_result = ConversionResult.FormatError;
				return ConversionResult.FormatError;
			}
			
			m_data = StringToByte(m_raw_data.Substring(9, 2*m_data_length));
			m_checksum = StringToByte(m_raw_data.Substring(9+2*m_data_length, 2))[0];
			
			
			//sum up all the data (including checksum)
			int sum = 0;
			
			for(int count = 0; count < full_set.Length; count++)
			{
				sum = sum + full_set[count];
			}
			
			if((sum & 0x000000FF) != 0)
			{
				//checksum is NOT okay
				m_last_conversion_result = ConversionResult.ChecksumError;
				return ConversionResult.ChecksumError;
			}
			

			
			if(m_type == 1)
			{
				m_last_conversion_result = ConversionResult.EOF;
				return ConversionResult.EOF;
			}
			
			m_last_conversion_result = ConversionResult.OK;
			return ConversionResult.OK;
		}
		
		public ConversionResult Result
		{
			get
			{
				return m_last_conversion_result;
			}
		}
		
		public byte[] Data
		{
			get
			{
				return m_data;
			}
		}
		
		public byte Type
		{
			get
			{
				return m_type;
			}
		}
		#endregion
		
		
		public static ConversionResult CheckLine(string line)
		{
			if(line[0] != ':')
				return ConversionResult.FormatError;
			
			if(line.Length < 11)
				return ConversionResult.FormatError;
			
			return ConversionResult.OK;
		}
		
		
		private byte[] StringToByte(string text_representation)
		{
			if((text_representation.Length % 2) != 0)
			{
				throw new Exception();
			}
			
			int length = text_representation.Length / 2;
			
			byte[] result = new byte[length];
			
			for(int counter = 0; counter <length; counter+= 1)
			{
				char ms = text_representation[2*counter];
				char ls = text_representation[2*counter + 1];
				
				result[counter] = (byte)(HexToNum(ms)*(byte)16 + HexToNum(ls));
			}
			
			return result;
		}
		
		private byte HexToNum(char hex_representation)
		{
			if(hex_representation >= '0' && hex_representation <='9')
			{
				return (byte)(hex_representation - 48);
			}
			else if(hex_representation >= 'A' && hex_representation <= 'F')
			{
				return (byte)(hex_representation - 55);
			}
			else 
			{
				throw new Exception();
			}
		}
	}
}

